package edu.xlh.travel.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import edu.xlh.travel.aipay.util.AlipayConfig;
import edu.xlh.travel.asynctask.AsyncTask;
import edu.xlh.travel.common.Result;
import edu.xlh.travel.dao.EmailDao;
import edu.xlh.travel.dao.OrdersDao;
import edu.xlh.travel.dao.ScenicDao;
import edu.xlh.travel.dao.TotalScoreDao;
import edu.xlh.travel.enumeration.EmailTypeEnum;
import edu.xlh.travel.enumeration.OrderStatusEnum;
import edu.xlh.travel.enumeration.OrderTypeEnum;
import edu.xlh.travel.pojo.*;
import edu.xlh.travel.pojo.querypojo.QueryOrders;
import edu.xlh.travel.service.OrdersService;
import edu.xlh.travel.service.ScenicService;
import edu.xlh.travel.service.ScoreDetailService;
import edu.xlh.travel.service.TotalScoreService;
import edu.xlh.travel.util.BigDecimalUtil;
import edu.xlh.travel.util.DateTimeUtil;
import edu.xlh.travel.util.IdWorker;
import edu.xlh.travel.util.PageResult;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;


import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
@Transactional
public class OrdersServiceImpl implements OrdersService {
    @Autowired
    private OrdersDao ordersDao;
    @Autowired
    private ScenicDao scenicDao;
    @Autowired
    private IdWorker idWorker;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private AsyncTask asyncTask;
    @Autowired
    private TotalScoreDao totalScoreDao;
    @Autowired
    private ScoreDetailService scoreDetailService;
    @Autowired
    private EmailDao emailDao;

    private static final String orderTypePrefix= OrderTypeEnum.scenicOrder.getCode();

    @Override
    public  Result buy(HttpSession session, Orders orders) {
    User user = (User)session.getAttribute("user");
    if (user == null) {
        return new Result(201);
    }
    Scenic scenic = scenicDao.findById(orders.getScenicId()).get();
    Integer stock = scenic.getStock();
    Integer qty = orders.getQty();
    //计算总金额
    BigDecimal payment = BigDecimalUtil.mul(scenic.getPrice(),qty);
    if (stock < qty) {
        return new Result(202);
    }else {
        Integer newStock = stock - qty;
        scenic.setStock(newStock);
        scenicDao.save(scenic);
    }
    //获取当前时间
    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    String date = df.format(new Date());// new Date()为获取当前系统时间，也可使用当前时间戳
    String userId = user.getId();
    orders.setId( idWorker.nextId()+"" );
    orders.setUserId(userId);
    orders.setUserName(user.getName());
    orders.setPhone(user.getMobile());
    orders.setScenicId(scenic.getId());
    orders.setScenicName(scenic.getName());
    orders.setPayment(payment);
    orders.setQty(qty);
    orders.setStatus("0");
    orders.setDelFlag("0");
    //orders.setPaytime(DateTimeUtil.strToDate(date,"yyyy-MM-dd HH:mm:ss"));
    orders.setModifyTime(DateTimeUtil.strToDate(date,"yyyy-MM-dd HH:mm:ss"));
    orders.setCreateTime(DateTimeUtil.strToDate(date,"yyyy-MM-dd HH:mm:ss"));
    //未支付
    orders.setPaytime(null);
    ordersDao.save(orders);

        //发邮件
        Email email = new Email()
                .setId(idWorker.nextId() + "")
                .setOrderId(orders.getId())
                .setSendOutUserId("")
                .setSendOutName("系统回复")
                .setReceiveUserId(user.getId())
                .setReceiveName(user.getName())
                .setSendTime(new Date())
                .setIsRead("0")
                .setDelFlag("0")
                .setEmailType(EmailTypeEnum.order.getCode())
                .setOrderType(OrderTypeEnum.scenicOrder.getCode());
        emailDao.save(email);

    System.out.println("主线程id: "+Thread.currentThread().getId());
    //设置已下单订单未支付期限
    //1.设置订单失效时间
    Calendar cal = Calendar.getInstance();
    cal.add(Calendar.MINUTE , 4);
    //int minute4later =  (int)(System.currentTimeMillis()/1000/60%60);
    System.out.println("四分钟后的时间"+cal.get(Calendar.MINUTE));
    //2.利用zset的score升序特性，将过期时间minute4later作为score
    stringRedisTemplate.opsForZSet().add("orderId" , orders.getId() ,cal.get(Calendar.MINUTE));
    //System.out.println(System.currentTimeMillis()/ 1000);
    //3.异步处理失效订
    asyncTask.doHandleExpiredOrder(OrderTypeEnum.scenicOrder.getCode());
    return  new Result(200);
}

    @Override
    public Result findAllOrders(HttpSession session) {
        User user = (User)session.getAttribute("user");
        List<Orders> ordersList = ordersDao.findByUserId(user.getId());
        return new Result(200 , ordersList);
    }

    @Override
    public Result findAll() {
        List<Orders> ordersList = ordersDao.findAll();
        if (ordersList!=null && ordersList.size()>0) {
            return new Result(0 , ordersList);
        }
        return new Result(201 , "返回景点列表失败");
    }

    @Override
    public Result findAll(int page, int size , HttpSession session) {
        User user = (User)session.getAttribute("admin");
        if (user == null) {
            return new Result(201,"会话过期，请重新登陆");
        }
        Specification specification = new Specification() {
            @Override
            public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
                List<Predicate> orderList = new ArrayList<>();
                orderList.add(criteriaBuilder.equal(root.get("delFlag"), "0"));
                return criteriaBuilder.and(orderList.toArray(new Predicate[orderList.size()]));
            }
        };
        PageRequest pageRequest =  PageRequest.of(page-1, size);
        Page<Orders> ordersPageList = ordersDao.findAll(specification , pageRequest);
        return new Result(0 , "分页数据获取成功" , new PageResult<Orders>(ordersPageList.getTotalElements(), ordersPageList.getContent()));
    }

    @Override
    public void updateStatusByOrderId(String status , String orderId) {
        ordersDao.updateStatusByOrderId(status , orderId);
    }

    @Override
    public void updateStockByOrderId(String orderId , int stock) {
        ordersDao.updateStockByOrderId(orderId , stock);
    }

    @Override
    public Orders findById(String orderId) {
        return ordersDao.findById(orderId).get();
    }

    @Override
    public Result cancelOrder(String orderId, HttpSession session) {
        User user = (User)session.getAttribute("user");
        if (user == null) {
            return new Result(201,"用户登录信息过期，请重新登录");
        }
        //获取当前时间
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String date = df.format(new Date());// new Date()为获取当前系统时间，也可使用当前时间戳
        Orders orders = new Orders().setId(orderId)
                .setModifyTime(DateTimeUtil.strToDate(date, "yyyy-MM-dd HH:mm:ss"))
                .setStatus(OrderStatusEnum.endTrade.getCode());
        try{
            ordersDao.updateOrderById(orders);
        }catch(Exception e){
            return new Result(202,"订单取消失败");
        }
            return new Result(200,"订单取消成功");
    }

    @Override
    public Integer getOrderCount() {
        return Math.toIntExact(ordersDao.count());
    }

    @Override
    public String toPayOrderPage(String orderId, HttpSession session , Model model) {
        Orders order = ordersDao.findById(orderId).get();
        model.addAttribute("order" , order);
        return "/user/payOrderPage";
    }

    @Override
    public String doPayForOrder(HashMap<String, String> orderInfo, HttpSession session) throws AlipayApiException {
        String orderId = (String)orderInfo.get("orderId");
        String payment = (String)orderInfo.get("payment");
        String payType = (String)orderInfo.get("payType");
        System.out.println("订单号："+orderId+"....."+"总金额："+payment);
        //发起支付
        Orders order = ordersDao.findById(orderId).get();
        //保存留言
        String remarks = orderInfo.get("remarks");
        ordersDao.saveRemarks(orderId, remarks);
        String scenicId = order.getScenicId();
        Scenic scenic = scenicDao.findById(scenicId).get();
        //获得初始化的AlipayClient
        AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);

        //设置请求参数
        AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
        alipayRequest.setReturnUrl(AlipayConfig.return_url);
        alipayRequest.setNotifyUrl(AlipayConfig.notify_url);

        //商户订单号，商户网站订单系统中唯一订单号，必填
        String out_trade_no = orderTypePrefix + orderId;
        //付款金额，必填;区分原价支付还是积分支付
        User user = (User) session.getAttribute("user");
        String userId = user.getId();
        String isVip = user.getIsVip();
        if ("1".equals(isVip)) {
            if ("2".equals(payType)) {
                TotalScore byUserId = totalScoreDao.findByUserId(userId);
                int totalScore = Integer.parseInt(byUserId.getTotalScore());
                if (totalScore >= 10)
                    payment = String.valueOf(Double.valueOf(payment) - 10.00);
                totalScore -= 10;
                totalScoreDao.updateTotalScoreByUserIdForPay(userId , String.valueOf(totalScore));
            }
        }
        String total_amount = payment;
        //订单名称，必填
        String subject = scenic.getName();
        //商品描述，可空
        String body = "用户订购商品个数：" + order.getQty();

        // 该笔订单允许的最晚付款时间，逾期将关闭交易。取值范围：1m～15d。m-分钟，h-小时，d-天，1c-当天（1c-当天的情况下，无论交易何时创建，都在0点关闭）。 该参数数值不接受小数点， 如 1.5h，可转换为 90m。
        String timeout_express = "30m";

        alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
                + "\"total_amount\":\""+ total_amount +"\","
                + "\"subject\":\""+ subject +"\","
                + "\"body\":\""+ body +"\","
                + "\"timeout_express\":\""+ timeout_express +"\","
                + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
        //请求
        String result = alipayClient.pageExecute(alipayRequest).getBody();
        return result;
    }

    @Override
    public void updateOrderByEntitiy(Orders order) {
        ordersDao.updateOrderByEntitiy(order);
    }

    @Override
    public int countByUserId(String userId) {
        return ordersDao.countByUserId(userId);
    }

    @Override
    public Result QueryOrdersByCondition(Integer page, Integer limit, QueryOrders queryOrders, HttpSession session) {
        User userFromSession = (User)session.getAttribute("admin");
        if (userFromSession == null) {
            return new Result(201,"会话过期，请重新登陆");
        }

        Specification specification = new Specification() {
            @Override
            public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
                List<Predicate> ordersList = new ArrayList<>();
                if (StringUtils.isNotEmpty(queryOrders.getId())) {
                    ordersList.add(criteriaBuilder.equal(root.get("id") , queryOrders.getId()));
                }
                if (StringUtils.isNotEmpty(queryOrders.getStatus())) {
                    ordersList.add(criteriaBuilder.like(root.get("status") , "%" + queryOrders.getStatus() + "%"));
                }
                if (queryOrders.getCreateTime() != null) {
                    String crateTime = DateTimeUtil.dateToStr(queryOrders.getCreateTime(), "yyyy-MM-dd");
                    String crateTimeStartTimeToStr = crateTime + " " + "00:00:00";
                    String crateTimeEndTimeToStr = crateTime + " " + "23:59:59";
                    Date crateTimeStartTimeToDate = DateTimeUtil.strToDate(crateTimeStartTimeToStr , "yyyy-MM-dd HH:mm:ss");
                    Date crateTimeEndTimeToDate = DateTimeUtil.strToDate(crateTimeEndTimeToStr , "yyyy-MM-dd HH:mm:ss");
                    ordersList.add(criteriaBuilder.between(root.get("createTime") ,  crateTimeStartTimeToDate , crateTimeEndTimeToDate));
                }
                if (queryOrders.getPaytime() != null) {
                    String paytime = DateTimeUtil.dateToStr(queryOrders.getPaytime(), "yyyy-MM-dd");
                    String paytimeStartTimeToStr = paytime + " " + "00:00:00";
                    String paytimeEndTimeToStr = paytime + " " + "23:59:59";
                    Date paytimeStartTimeToDate = DateTimeUtil.strToDate(paytimeStartTimeToStr , "yyyy-MM-dd HH:mm:ss");
                    Date paytimeEndTimeToDate = DateTimeUtil.strToDate(paytimeEndTimeToStr , "yyyy-MM-dd HH:mm:ss");
                    ordersList.add(criteriaBuilder.between(root.get("paytime") ,  paytimeStartTimeToDate , paytimeEndTimeToDate));
                }
                return criteriaBuilder.and(ordersList.toArray(new Predicate[ordersList.size()]));
            }
        };
        Pageable pageable= PageRequest.of(page-1,limit);
        Page<Orders> ordersListPage  = ordersDao.findAll(specification , pageable);
        if (ordersListPage!=null) {
            return new Result(0 , "景点订单分页数据获取成功" , new PageResult<Orders>(ordersListPage.getTotalElements(), ordersListPage.getContent()));
        }
        return new Result(201 , "景点订单列表失败");
    }

    @Override
    public Result deleteById(String id, HashMap hashMap, HttpSession session) {
        User user = (User)session.getAttribute("admin");
        if (user == null) {
            return new Result(201,"会话过期，请重新登陆");
        }
        String orderId = (String) hashMap.get("id");
        if (id.equals(orderId)) {
            try {
                ordersDao.updateOrderDelFlagByOrderId(orderId , "1");
            } catch (Exception e) {
                return new Result(201 , "id为"+orderId+"的景点订单记录"+"删除失败");
            }
        } else {
            return new Result(202 , "id为"+orderId+"的景点订单id有问题");
        }
        return new Result(200 , "id为"+orderId+"的景点订单记录"+"删除成功");
    }

    @Override
    public Result batchDelete(JSONArray jsonArray, HttpSession session) {
        List<Orders> orderList = JSON.parseArray(jsonArray.toJSONString() , Orders.class);
        try {
            orderList.stream().iterator().forEachRemaining(order -> {
                ordersDao.updateOrderDelFlagByOrderId(order.getId() , "1");
            });
        }catch (Exception e) {
            return new Result(201);
        }
        return new Result(200);
    }

    @Override
    public Result consume(String orderId, HttpSession session) {
        User userFromSession = (User)session.getAttribute("user");
        if (userFromSession == null) {
            return new Result(201,"会话过期，请重新登陆");
        }
        try {
            ordersDao.updateStatusByOrderId(OrderStatusEnum.scenicConsumingSuccess.getCode(), orderId);
        }catch (Exception e) {
            return new Result(202 , "景点消费失败");
        }
        return new Result(200 , "景点消费成功");
    }

    @Override
    public Result findByOrderId(String orderId, String emailId, HttpSession session) {
        emailDao.updateIsReadById(emailId , "1");
        Orders orders = ordersDao.findById(orderId).get();
        HashMap  hashMap  = new HashMap();
        Scenic scenic = scenicDao.findById(orders.getScenicId()).get();
        hashMap.put("order" , orders);
        hashMap.put("scenic" , scenic);
        String status = orders.getStatus();
        String statusDesc = null;
        Email email = emailDao.findById(emailId).get();

        String emailTypeDesc = null;

        switch (email.getEmailType()) {
            case "0" : {emailTypeDesc = EmailTypeEnum.order.getDesc() ; break;}
            case "1" : {emailTypeDesc = EmailTypeEnum.arrange.getDesc() ; break;}
            case "2" : {emailTypeDesc = EmailTypeEnum.relet.getDesc() ; break;}
            case "4" : {emailTypeDesc = EmailTypeEnum.checkOutSuccess.getDesc() ; break;}

        };

        if (OrderStatusEnum.arrangeing.getCode().equals(status))  {
            statusDesc = OrderStatusEnum.arrangeing.getDesc();
        } else if (OrderStatusEnum.staying.getCode().equals(status)) {
            statusDesc = OrderStatusEnum.staying.getDesc();
        }
        hashMap.put("statusDesc" , emailTypeDesc);
        return new Result(200 , hashMap);
    }
}
